/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_OCTREE_H_
#define M_OCTREE_H_

#include "aabb.h"
#include "weakptr.h"
#include "types.h"
#include "octreerenderable.h"

#include <list>
#include <string>
#include <map>
#include <vector>

namespace Engine
{
	/**
	*   Static geometry triangle helper
	*/
	struct Triangle
	{
		Vector3<REAL> v0;
		Vector3<REAL> v1;
		Vector3<REAL> v2;
		unsigned short index0;
		unsigned short index1;
		unsigned short index2;
		std::string material;
	};

	/**
	*   Octree datastructure for managing scene nodes.
	*/
	class Octree
	{
	public:
		typedef std::list<OctreeNode* > NodeList;
		typedef std::vector<unsigned short> IndexVector;
		typedef std::map<std::string, IndexVector> StaticGeomMap;

		Octree(Octree* p, bool haveStatic = false);

		~Octree();

		/**
		*   Adds an Octree scene node to this octree level.
		*/
		void AddNode( OctreeNode* node );

		/**
		*   Removes an Octree scene node to this octree level.
		*/
		void RemoveNode( OctreeNode* node );

		/**
		*    Returns the number of scene nodes attached to this octree
		*/
		int GetNumNodes(){return m_numNodes;}

		/**
		*   Determines if this octree is twice as big as the given box.
		*/
		bool IsTwiceSize( const AABB &box ) const;

		/**
		*   Returns the appropriate indexes for the child of this octree into which the box will fit.
		*/
		void GetChildIndexes(const AABB& box, int* x, int* y, int* z) const;

		/**
		*   Creates the AxisAlignedBox used for culling this octree.
		*/
		void GetCullBounds(AABB* bounds) const;

		/**
		*   Set bounding box.
		*/
		void SetBBox(const AABB& box){m_box = box;}

		/**
		*   Return bounding box
		*/
		const AABB& GetBBox(){return m_box;}

		/**
		*   Set octree dimensions
		*/
		void SetHalfSize(const Vector3<REAL>& size){m_halfSize = size;}

		/**
		*   Return iterator for walking through childs
		*/
		NodeList::iterator GetNodesBegin(){return m_nodes.begin();}

		/**
		*   Return iterator for walking through childs
		*/
		NodeList::iterator GetNodesEnd(){return m_nodes.end();}

		/**
		*   Return child octree(octant)
		*/
		Octree* GetChildren(size_t x, size_t y, size_t z){return m_children[x][y][z];}

		/**
		*   Set child octree(octant)
		*/
		void SetChildren(size_t x, size_t y, size_t z, Octree* octant){m_children[x][y][z] = octant;}

		/**
		*   Add static geometry triangle
		*/
		void AddTriangle(const Triangle& tri);

		/**
		*   Update static geometry
		*/
		void UpdateStaticGeom(RenderablesMap* renderables) const;

		/**
		*   Return true if has static geom
		*/
		bool HaveStatic() const;

		/**
		*   Notify octant that he have static geom
		*/
		void SetHaveStatic(bool val);

	protected:

		/**
		*   Increments the overall node count of this octree and all its parents
		*/
		inline void Ref()
		{
			++m_numNodes;

			if ( m_parent != 0 )
				m_parent->Ref();
		};

		/**
		*   Decrements the overall node count of this octree and all its parents
		*/
		inline void UnRef()
		{
			--m_numNodes;

			if ( m_parent != 0 )
				m_parent->UnRef();
		};

	private:

		AABB m_box; ///< The bounding box of the octree

		Vector3<REAL> m_halfSize; ///< Vector containing the dimensions of this octree / 2

		Octree* m_children[ 2 ][ 2 ][ 2 ]; ///< 3D array of children of this octree.

		NodeList m_nodes; ///< Public list of SceneNodes attached to this particular octree

		int m_numNodes; ///< number of SceneNodes in this octree and all its children.

		Octree* m_parent; ///< parent octree

		StaticGeomMap m_staticGeom; ///< static geometry associated with octant.

		bool m_haveStatic; ///< True if have static or childs have static

	};
}

#endif
